﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace IETTortak
{
    public class AsyncHTMLDownloader
    {
        private class DosyaKeyDownloadProgress
        {
            public int Tümü;
            public int Tamamlanan;
            public DosyaKeyDownloadProgress(int Tamamlanan, int Tümü)
            {
                this.Tamamlanan = Tamamlanan;
                this.Tümü = Tümü;
            }
        }

        private const int numberOfThreadsToUse = 10;
        private List<string> dosyalar;
        private Semaphore semaParse = new Semaphore(numberOfThreadsToUse, numberOfThreadsToUse);
        private const int DosyaPerThread = 10;


        public delegate void ALVHataHandler(string key, string message);
        public delegate void ALVDurumDeğiştiHandler(string key);
        public delegate void ALVEkle(string key, string message);

        public delegate void HTMLEkleΗandler(string url, byte[] data);

        public event ALVDurumDeğiştiHandler Processing;
        public event ALVDurumDeğiştiHandler Success;
        public event ALVHataHandler Hata;
        public delegate void DosyalarİndirildiHandler();

        public event DosyalarİndirildiHandler Dosyalarİndirildi;
        public event ALVEkle Ekle;

        public event HTMLEkleΗandler HTMLEkle;

        public delegate void UpdateProgressHandler(string key, int tamamlanan, int tümü);
        public event UpdateProgressHandler UpdateProgress;

        public int HTMLCount
        {
            get { return dosyalar.Count; }
        }

        protected void OnUpdateProgress(string key, int tamamlanan, int tümü)
        {
            if (UpdateProgress != null)
                UpdateProgress(key, tamamlanan, tümü);
        }

        protected void OnDosyalarİndirildi()
        {
            if (Dosyalarİndirildi != null)
                Dosyalarİndirildi();
        }

        protected void OnHTMLEkle(string url, byte[] data)
        {
            if (HTMLEkle != null)
                HTMLEkle(url, data);
        }

        protected void OnProcessing(string key)
        {
            if (Processing != null)
                Processing(key);
        }

        protected void OnSuccess(string key)
        {
            if (Success != null)
                Success(key);
        }

        protected void OnHata(string key, string message)
        {
            if (Hata != null)
                Hata(key, message);
        }

        protected void OnEkle(string key, string message)
        {
            if (Ekle != null)
                Ekle(key, message);
        }

        private string GetGüzergahURL(string hatNo)
        {
            Random r = new Random();
            return string.Format(
                "http://www.iett.gov.tr/saat/orer.php?hatcode={0}&hid=hat&BtnDetay.x={1}&BtnDetay.y={2}", URLEncode(hatNo), r.Next(50), r.Next(20));
        }

        private string GetSaatURL(string hatNo)
        {
            return string.Format("http://www.iett.gov.tr/saat.php?hat={0}", URLEncode(hatNo));
        }

        private string URLEncode(string s)
        {
            return s.Replace("Ş", "%DE").Replace("İ", "%DD").Replace("Ö", "%D6").Replace("Ç", "%C7").Replace("Ü", "%DC");
        }

        private string URLDecode(string s)
        {
            return s.Replace("%DE", "Ş").Replace("%DD", "İ").Replace("%D6", "Ö").Replace("%C7", "Ç").Replace("%DC", "Ü");
        }

        public AsyncHTMLDownloader(string[] lstURL)
        {
            List<string> ret = new List<string>();
            foreach (string s in lstURL)
            {
                // saat URL ve güzerfag URL ekle
                ret.Add(GetGüzergahURL(s));
                ret.Add(GetSaatURL(s));                
            }
            dosyalar = ret;
        }

        public void Start()
        {
            Thread t = new Thread(new ThreadStart(StartProcessing));
            t.Start();
        }

        private void StartProcessing()
        {
            while (dosyalar.Count > 0)
            {
                List<string> dosyalarToProcess = dosyalar.GetRange(0, Math.Min(DosyaPerThread, dosyalar.Count));
                dosyalar.RemoveRange(0, Math.Min(DosyaPerThread, dosyalar.Count));

                semaParse.WaitOne();

                ThreadPool.QueueUserWorkItem(new WaitCallback(Dosyalarıİşle), dosyalarToProcess);
            }

            for (int ctr = 0; ctr < numberOfThreadsToUse; ctr++)
            {
                semaParse.WaitOne();
            }
            semaParse.Release(numberOfThreadsToUse);
            OnDosyalarİndirildi();
        }

        private Hashtable htDosyaKeyProgress = new Hashtable();
        private Dictionary<string, string> dictDosyaKeyKey = new Dictionary<string, string>();

        private void Dosyalarıİşle(object state)
        {
            string key = Guid.NewGuid().ToString();
            htDosyaKeyProgress.Add(key, new Dictionary<string, DosyaKeyDownloadProgress>());

            try
            {
                List<string> işlenecekDosyalar = state as List<string>;
                OnEkle(key, string.Format("İndirilen dosya sayısı: {0} - ThreadID: ", işlenecekDosyalar.Count) + Thread.CurrentThread.ManagedThreadId.ToString());
                OnProcessing(key);
                int tamamlanan = 0;
                foreach (string s in işlenecekDosyalar)
                {
                    string dosyaKey = s;
                    dictDosyaKeyKey.Add(dosyaKey, key);
                    (htDosyaKeyProgress[key] as Dictionary<string, DosyaKeyDownloadProgress>).Add(dosyaKey, new DosyaKeyDownloadProgress(0, 1));

                    RJH.DownloadThread dt = new RJH.DownloadThread(dosyaKey, s);
                    dt.ProgressCallback+=new RJH.DownloadProgressHandler(dt_ProgressCallback);
                    dt.CompleteCallback+=new RJH.DownloadCompleteHandler(dt_CompleteCallback);
                    dt.Download();                    
                    //tamamlanan++;
                    //OnUpdateProgress(key, tamamlanan, işlenecekDosyalar.Count);
                }
                //OnSuccess(key);

            }
            catch (Exception ex)
            {
                OnHata(key, ex.Message);
                semaParse.Release();// hata olursa release et
            }
            finally
            {
                //semaParse.Release(); 
            }
        }

        void dt_ProgressCallback(string key, int bytesRead, int totalBytes)
        {
            if (key == null) return;
            (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>)[key].Tamamlanan = bytesRead;
            (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>)[key].Tümü = totalBytes;

            int total = 0;
            int tamamlanan = 0;
            foreach (DosyaKeyDownloadProgress dkdp in (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>).Values)
            {
                if (dkdp.Tümü > 1)
                    total += dkdp.Tümü;
                if (dkdp.Tamamlanan > dkdp.Tümü)
                    total += (int)(dkdp.Tamamlanan * 1.25);
                tamamlanan += dkdp.Tamamlanan;
            }
            if (total == 0) total = 1;
            OnUpdateProgress(dictDosyaKeyKey[key], tamamlanan, total);
        }

        void dt_CompleteCallback(string key, byte[] dataDownloaded)
        {
            (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>)[key].Tamamlanan = dataDownloaded.Length;
            (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>)[key].Tümü = dataDownloaded.Length;

            OnHTMLEkle(key, dataDownloaded);

            // all done?
            bool allDone = false;
            foreach (DosyaKeyDownloadProgress dkdp in (htDosyaKeyProgress[dictDosyaKeyKey[key]] as Dictionary<string, DosyaKeyDownloadProgress>).Values)
            {
                if (dkdp.Tamamlanan != dkdp.Tümü)
                {
                    allDone = false;
                    break;
                }
                allDone = true;
            }

            if (allDone)
            {
                OnSuccess(dictDosyaKeyKey[key]);
                semaParse.Release();
            }
        }
    }
}
